# [十七] Spring MVC 核心调用流程-返回值解析
# 返回值解析
概述
当反射调用成功后,有可能方法会有返回值,而返回值处理也是一个比较重要的事情,根据什么样的方式把返回值响应回去,返回值响应时有可能是数据有可能是界面,如果返回数据的话,要把返回值解析成对应的格式,例如如果返回值是一个 list 对象,就需要解析这个 list 对象把 list 对象解析成 json 格式。返回值解析讨论跟入参解析基本上类似。
返回值解析
原理 :
1、把返回值封装成对象,对象跟 MethodParameter
对象差不多,里面包括参数名称、类型、参数注解等等信息
2、根据返回值类型用策略模式找到一个解析类
3、用这个解析类解析
4、这块跟两个比较典型的就差不多了,一个是带@ResponseBody
注解的,一个是直接返回字符串响应一个界面的,里面涉及到一个ModelAndViewContainer
容器,这个容器会把视图名称设置到里面,已经 Model 数据,就是响应到界面的数据也会放到这个容器中
进入
invokeHandlerMethod
方法所在类:org.springframework.web.servlet.mvc.method.annotation.
RequestMappingHandlerAdapter
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// Model工厂,收集了@ModelAttribute注解的方法
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// ...... 省略
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 调用有 @ModelAttribute注解的方法。每次请求都会调用有 @ModelAttribute
// 注解的方法,把 @ModelAttribute注解的方法的返回值存储
// 到ModelAndViewContainer对象的 map中了
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// ...... 省略
// Controller方法调用,重点看看
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 返回业务处理,返回json格式还是跳转页面
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
进入
invokeAndHandle
方法所在类:org.springframework.web.servlet.mvc.method.annotation.
ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 具体调用逻辑,重点看
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
// 设置有 @ResponseBody 标注的 请求处理为 true
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 设置没有 @ResponseBody 标注的 请求处理为 false,返回处理的时候根据这个属性判断
// 是否返回页面 还是 Json 格式
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 返回值处理业务,重点看
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
进入
handleReturnValue
方法所在类:org.springframework.web.method.support.
HandlerMethodReturnValueHandlerComposite
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 找到一个合适的返回值处理方法,匹配过程
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 具体处理返回值业务,由子类实现,默认有15种返回值处理类
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
知识点
handleReturnValue
方法种有一个mavContainer
参数,这个参数类型是ModelAndViewContainer
,内部维护了一个ModelMap
用于存放@ModelAttribute
注解的方法的返回值。比如:
/*
* TODO:所有的请求都会先调用这个方法,把方法执行的返回值存到 areaBean 的map容器中。
* */
@ModelAttribute("areaBean")
public List<ConsultConfigArea> queryArea(@RequestParam String areaCode) {
Map map = new HashMap<>();
map.put("areaCode",areaCode);
return areaService.queryAreaFromDB(map);
}
/*
* TODO:@ModelAttribute 来进行初始化赋值
* */
public @ResponseBody String checkArea (@ModelAttribute("areaBean") ConsultConfigArea area) {
// 通过@ModelAttribute 拿到缓存中名字为 areaBean的对象 赋值给 area,
// 然后就可以直接调用 area.getXX() 获取到值了
System.out.println("=======业务处理======="+area.getAreaCode();
}
进入
selectHandler
方法所在类:org.springframework.web.method.support.
HandlerMethodReturnValueHandlerComposite
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 典型的策略模式
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
知识点
HandlerMethodReturnValueHandler
接口也是典型的策略模式,和参数解析的流程基本一致,也有一个supportsReturnType
匹配方法和handleReturnValue
处理返回值业务的方法。
返回
invokeHandlerMethod
方法进入
getModelAndView
方法所在类:org.springframework.web.servlet.mvc.method.annotation.
RequestMappingHandlerAdapter
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 判断 ModelAndViewContainer 类中的 requestHandled 属性是否为 true,
// 如果有 @ResponseBody 则直接返回空,不需要跳转页面
if (mavContainer.isRequestHandled()) {
return null;
}
// 否则处理跳转页面
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
扩展阅读
HandlerMethodReturnValueHandler
返回值解析器,结构和参数解析器基本一致,Spring中默认有15种返回值解析器,来对应完成某种返回值的解析工作。添加过程是Spring MVC
启动实例化后,通过RequestMappingHandlerAdapter
类的afterPropertiesSet
方法调用getDefaultReturnValueHandlers
添加到HandlerMethodReturnValueHandler
解析器中的。
getDefaultReturnValueHandlers
方法源码
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// Single-purpose return value types
// 默认基本类型的返回值
// 处理返回类型 ModelAndView
handlers.add(new ModelAndViewMethodReturnValueHandler());
// 处理返回类型 Model
handlers.add(new ModelMethodProcessor());
// 处理返回类型 View
handlers.add(new ViewMethodReturnValueHandler());
// 处理返回类型 ResponseBodyEmitter
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
// 处理返回类型 StreamingResponseBody
handlers.add(new StreamingResponseBodyReturnValueHandler());
// 处理返回类型 HttpEntity 和 RequestEntity
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// 处理返回类型 HttpHeaders
handlers.add(new HttpHeadersReturnValueHandler());
// 处理返回类型 Callable
handlers.add(new CallableMethodReturnValueHandler());
// 处理返回类型DeferredRequest 和 ListenableFuture
handlers.add(new DeferredResultMethodReturnValueHandler());
// 处理返回类型WebAsyncTask
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
// 带注解的返回值类型
// 处理 @ModelAttribute
handlers.add(new ModelAttributeMethodProcessor(false));
// 处理 @ResponseBody
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
// 处理返回类型 Void.class 和 CharSequense.class
handlers.add(new ViewNameMethodReturnValueHandler());
// 处理返回类型 Map
handlers.add(new MapMethodProcessor());
// Custom return value types
// 自定义返回值类型
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
# 异常解析
概述
类上面加上@ControllerAdvice("com.xx.xx")
注解,这个包定义就是只对这个包里 面的Controller
生效。然后类里面的方法加上
- @ExceptionHandler({ArrayIndexOutOfBoundsException.class})
- @ExceptionHandler({NullPointerException.class})
表示这个方法当调用过程中出现注解里面定义的异常时会被调用到,这些方法就是对异常处理的方法。
@ControllerAdvice
原理
1、收集注解包装成类
2、建立@ExceptionHandler 中异常和 Method 的映射关系
3、根据出现的异常从映射关系中找到对应的 Method 对象
4、反射调用,这个调用逻辑跟 Controller 里面具体方法调用逻辑一模一样
为什么要把 ModelAndViewContainer mavContainer 做为参数放到返回值解析中?
在ServletInvocableHandlerMethod
类的invokeAndHandle
处理方法调用的时候中会设置一个属性,如果有@ResponseBody
注解的标注 mavContainer.setRequestHandled(true);
,否则设置为 mavContainer.setRequestHandled(false);
这样调用getModelAndView
返回业务处理的时候就可以根据ModelAndViewContainer
类中的requestHandled
属性来判断是否返回json格式还是返回页面。